/*
 *项目名称：模拟实现操作界面（即用户名 主机名 ...）待输入命令的shell脚本
 *项目作者：新晓·故知
 *时间：2022.10.01—2022.10.02
 * 
 * */

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>


#define NUM 1024
#define SIZE 128
#define SEP " "            //设置分隔符

char command_line[NUM];    //保存输入的字符串
char *command_args[SIZE];  //指针数组，将字符串切割成子串，将子串的首地址放到数组

char env_buffer[NUM];      //just for test environ

//对应的shell内建命令
int ChangeDir(const char* new_path)
{
  chdir(new_path);
  return 0;     //调用成功
}
//对应的putenv函数
void  PutEnvInMyShell(char* new_path)
{
  putenv(new_path);
}

int main()
{
  //shell 操作界面等待的命令待显示，本质上就是一个死循环（先打印，再等待输入）
  while(1)
  {
    //用户名，主机名这些属性等其实是系统调用接口，这里我们先不关心获取这些属性的接口
    //如果有时间深入研究，可以在网上搜素“Linux获取用户名的系统调用接口”
    
    //1.显示提示符
    printf("[新晓故知@我的主机名 当前目录]# ");
    fflush(stdout); //刷新缓冲区
    
    //2.获取用户输入
    memset(command_line,'\0',sizeof(command_line)*sizeof(char));
    fgets(command_line,NUM,stdin);  //键盘，标准输入，stdin,获取到的是C风格的字符串，'\0'
    command_line[strlen(command_line)-1]='\0';  //清空\n (输入指令（字符串等）结束后，敲击回车键,而回车也是字符，但不可显)
   
    //3. "ls -a -l -i" —> "ls","-a","-l","-i"字符串切分
    command_args[0] = strtok(command_line,SEP);   //strtok切分字符串
    int index = 1;
    //给ls命令添加颜色
    if(strcmp(command_args[0],"ls") == 0)
      command_args[index++] = (char*)"--color=auto";

    // = 虽然报错，但是故意这么写的
    // strtok 1.截取成功，返回字符串起始地址 2.截取失败，返回NULL
    //
    while(command_args[index++] = strtok(NULL,SEP));

    // //for debug  仅用于测试是否截取成功
    // for(int i=0;i<index;i++)
    // {
    //   printf("%d : %s\n",i,command_args[i]);
    // }
    
    //4.
    //这里不直接进行替换的原因是：一旦直接替换，那整个shell就会替换，原有的配置被更改
    //交给子进程，子进程能够看到
    if(strcmp(command_args[0],"cd")==0 && command_args[1] != NULL)
    {
      ChangeDir(command_args[1]);  //让调用方进行路径切换！（父进程的路径切换）
      continue;
    }
    //存储导入的环境变量内容
    if(strcmp(command_args[0],"export") == 0 && command_args[1] != NULL)
    {
      //目前，环境变量信息在command_line,会被清空
      //此处我们需要自己保存一下环境变量内容
      strcpy(env_buffer,command_args[1]);
      PutEnvInMyShell((char*)env_buffer);         //export myval=999, 这里export是[0],myval=999是[1]
      continue;
    }


    //5.创建进程，执行
    pid_t id=fork();
    if(id == 0)
    {
      //子进程
      //6.程序替换
      execvp(command_args[0],command_args); //comman_args[0]就是我们要保存的名字
      exit(1);   //如果执行到这里，子进程一定替换失败，因为一旦替换成功，子进程就会执行新的程序去了
    }
    int status = 0;
    pid_t ret=waitpid(id,&status,0);
    if(ret>0)
    {
      printf("等待子进程成功：signal: %d, return code: %d\n",status&0x7F,(status>>8)&0xFF);
    }
  }
  //end while

  return 0;
}
